﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace _443_Server
{
	public class Player
	{
		public Socket Socket { get; set; }
		public NetworkStream NetworkInStream { get; set; }
		public NetworkStream NetworkOutStream { get; set; }
		public Stream InStream { get; set; }
		public Stream OutStream { get; set; }

		public string Username { get; set; }
		public string Password { get; set; }
		public long EncodedName { get; set; }

		public Location Location { get; set; }
		public OutPackets OutPackets { get; set; }

		public bool Teleported { get; set; }
		public int UpdateFlags { get; set; }

		public Player(Socket s)
		{
			Socket = s;
			NetworkInStream = new NetworkStream(s);
			NetworkOutStream = new NetworkStream(s);

			InStream = new Stream();
			OutStream = new Stream();

			Location = new Location(3333, 3333);

			OutPackets = new OutPackets(this);
			Teleported = true;
		}

		public void Startup()
		{
			try
			{
				Login.PerformLogin(this);
				UpdateFlags = 0x40; //appearance update
				OutPackets.SendMapRegion();
				OutPackets.SendPingPacket();
				OutPackets.SendAllTabs();
				OutPackets.SendMessage("Welcome to the 443 test server by Lin.");
				OutPackets.ShowInterface(227);
				OutPackets.SetInterfaceText(227, 30, "Test!");
				//PlayerUpdating.UpdatePlayer(this);
				Flush();
				new Thread(new ThreadStart(Cycle)).Start();
			}
			catch (Exception ex)
			{
				Disconnect(ex);
			}
		}

		public void Cycle()
		{
			System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
			Exception e = null;
			while (true)
			{
				watch.Start();
				try
				{
					for (int i = 0; i < 10; i++)
					{
						int available = Socket.Available;
						if (available > 0)
						{
							int packet = NetworkInStream.ReadByte();
							int size = PacketParser.PACKET_LENGTHS[packet];
							if (size == -1)
							{
								size = NetworkInStream.ReadByte();
							}
							else if (size == -2)
							{
								size = NetworkInStream.ReadByte() * 0x100 + NetworkInStream.ReadByte();
							}
							else if (size < 0)
							{
								throw new Exception("Unknown packet size for packet " + packet + "!");
							}
							Fill(size);

							PacketParser.ParsePacket(this, packet, size);
						}
						else
							break;
					}

					PerformCycle();

					if (OutStream.BufferLocation > 0)
						Flush();
				}
				catch (Exception ex)
				{
					e = ex;
					break;
				}

				watch.Stop();
				if (600 - watch.ElapsedMilliseconds > 0)
					Thread.Sleep(600 - (int)watch.ElapsedMilliseconds);
				watch.Reset();
			}

			Disconnect(e);
		}

		public void PerformCycle()
		{
			PlayerUpdating.UpdatePlayer(this);
		}

		public void Flush()
		{
			NetworkOutStream.Write(OutStream.Buffer, 0, OutStream.BufferLocation);
			OutStream.BufferLocation = 0;
		}

		public void Fill(int amount)
		{
			InStream.BufferLocation = 0;
			NetworkInStream.Read(InStream.Buffer, 0, amount);
		}

		public void Disconnect(Exception ex)
		{
			Console.WriteLine(Username + " has logged out" + (ex != null ? ": " + ex.Message + " at " + ex.StackTrace + "." : "."));
			try
			{
				Socket.Close();
				NetworkInStream.Close();
				NetworkOutStream.Close();
			}
			catch (Exception)
			{
			}

			Socket = null;
			NetworkInStream = null;
			NetworkOutStream = null;
			InStream = null;
			OutStream = null;
		}
	}
}
